//! RK3588 GMAC configuration wrapper for EmbedFire LubanCat 5 BTB

use ethernet_rk3588::{Delay, LinkSpeed, PhyInterface, Rk3588Gmac};
use kspin::SpinNoIrq;
use log::{error, info};

use crate::mem::phys_to_virt;

const SYS_GRF_BASE: usize = 0xFD58_C000;
const PHP_GRF_BASE: usize = 0xFD5B_0000;
const GMAC0_BASE: usize = 0xFE1B_0000;
const GMAC1_BASE: usize = 0xFE1C_0000;

const GMAC0_DELAY: Delay = Delay::new(0x26, 0x00);
const GMAC1_DELAY: Delay = Delay::new(0x30, 0x00);

/// Board-level Ethernet controller wrapper.
pub struct EthernetController {
    gmacs: [SpinNoIrq<Rk3588Gmac>; 2],
}

impl EthernetController {
    /// Create controller with GMAC instances mapped into the linear virtual
    /// space.
    pub const fn new() -> Self {
        let sys_grf_vaddr = phys_to_virt(axplat::mem::pa!(SYS_GRF_BASE)).as_usize();
        let php_grf_vaddr = phys_to_virt(axplat::mem::pa!(PHP_GRF_BASE)).as_usize();
        let gmac0_vaddr = phys_to_virt(axplat::mem::pa!(GMAC0_BASE)).as_usize();
        let gmac1_vaddr = phys_to_virt(axplat::mem::pa!(GMAC1_BASE)).as_usize();

        let gmac0 =
            unsafe { Rk3588Gmac::new(GmacId::Gmac0, gmac0_vaddr, sys_grf_vaddr, php_grf_vaddr) };
        let gmac1 =
            unsafe { Rk3588Gmac::new(GmacId::Gmac1, gmac1_vaddr, sys_grf_vaddr, php_grf_vaddr) };

        Self {
            gmacs: [SpinNoIrq::new(gmac0), SpinNoIrq::new(gmac1)],
        }
    }

    /// Apply default RGMII configuration for both GMAC instances.
    pub fn init(&self) {
        info!("Configuring RK3588 GMAC controllers (RGMII-RXID mode)...");

        if let Err(e) =
            self.configure_rgmii(GmacId::Gmac0, PhyInterface::RgmiiRxId, GMAC0_DELAY)
        {
            error!("GMAC0 configuration failed: {:?}", e);
        }
        if let Err(e) =
            self.configure_rgmii(GmacId::Gmac1, PhyInterface::RgmiiRxId, GMAC1_DELAY)
        {
            error!("GMAC1 configuration failed: {:?}", e);
        }
    }

    /// Configure the given GMAC for RGMII operation with board-specific delays.
    pub fn configure_rgmii(
        &self,
        id: GmacId,
        interface: PhyInterface,
        delay: Delay,
    ) -> ethernet_rk3588::Result<()> {
        self.with_gmac(id, |gmac| {
            gmac.configure_rgmii(interface, delay)?;
            gmac.set_speed(interface, LinkSpeed::Speed1000)?;
            Ok(())
        })
    }

    /// Configure the given GMAC for RMII operation.
    ///
    /// For RMII setups, callers must also invoke [`set_clock_selection`] to
    /// toggle the external reference clock and drive strength.
    pub fn configure_rmii(&self, id: GmacId) {
        self.with_gmac(id, |gmac| gmac.configure_rmii());
    }

    /// Update GMAC clock selection (CRU or external) and gate control.
    pub fn set_clock_selection(&self, id: GmacId, clock_input: bool, enable: bool) {
        self.with_gmac(id, |gmac| gmac.set_clock_selection(clock_input, enable));
    }

    /// Update the clock divisors to match the negotiated link speed.
    pub fn set_link_speed(
        &self,
        id: GmacId,
        interface: PhyInterface,
        speed: LinkSpeed,
    ) -> ethernet_rk3588::Result<()> {
        self.with_gmac(id, |gmac| gmac.set_speed(interface, speed))
    }

    /// Inspect an arbitrary SYS_GRF register associated with the GMAC block.
    pub fn read_sys_grf(&self, offset: usize) -> u32 {
        self.with_gmac(GmacId::Gmac0, |gmac| gmac.read_sys_grf(offset))
    }

    /// Inspect an arbitrary PHP_GRF register associated with the GMAC block.
    pub fn read_php_grf(&self, offset: usize) -> u32 {
        self.with_gmac(GmacId::Gmac0, |gmac| gmac.read_php_grf(offset))
    }

    fn with_gmac<R>(&self, id: GmacId, f: impl FnOnce(&mut Rk3588Gmac) -> R) -> R {
        let mut guard = match id {
            GmacId::Gmac0 => self.gmacs[0].lock(),
            GmacId::Gmac1 => self.gmacs[1].lock(),
        };
        f(&mut *guard)
    }
}

static ETHERNET_CONTROLLER: EthernetController = EthernetController::new();

pub use ethernet_rk3588::{
    Delay as GmacDelay, GmacId, LinkSpeed as GmacLinkSpeed, PhyInterface as GmacPhyInterface,
};

/// Get the global Ethernet controller reference.
pub fn ethernet_controller() -> &'static EthernetController {
    &ETHERNET_CONTROLLER
}

/// Initialize both GMAC instances in their default RGMII configuration.
pub fn init() {
    ethernet_controller().init();
}
